Borrowed pointers | 借贷指针

原文: https://github.com/nrc/r4cppp/blob/master/borrowed.md 翻译者: Scott Huang 翻译日期: Agust 26, 2015 于厦门

在上一篇文章中,我介绍了独特的指针(所有权指针)。这一次我会谈谈另一种更常见于大多数Rust程序的指针:借贷指针(或借用的引用,或只是引用)。

如果我们想有一个引用指向现有的值(而不是创建一个在堆上的新值并指向它,作为所有权指针),我们必须使用&,一个借来的引用。这可能是Rust最常见的一种指针,如果你想用某些东西来替代一个C++指针或引用(例如,通过引用传递函数的参数),这可能就是它。

我们使用&操作符创建一个借来的引用,并表示参考类型,并用*来解引用。自动解引用规则也应用于借贷指针,和所有权指针一样。例如,

fn foo() {
    let x = &3;   // type: &i32  类型: &i32
    let y = *x;   // 3, type: i32  3, 类型: i32
    bar(x, *x);
    bar(&y, y);
}

fn bar(z: &i32, i: i32) {
    // ...
}

&操作符不分配内存(我们只创建一个借贷指针来引用现有值),如果借贷指针超出了使用范围,没有内存被删除。

借用的引用不是独一无二的-你可以有多个借来的引用指向同一值。例如,

fn foo() {
    let x = 5;                // type: i32
    let y = &x;               // type: &i32
    let z = y;                // type: &i32
    let w = y;                // type: &i32
    println!("These should all 5: {} {} {}", *w, *y, *z);
}

就像值,借用的引用是默认不可变的。你也可以使用&mut来获取可变的引用,或表示可变的引用类型。 可变借贷引用是独一无二的(你只可以有一个唯一的可变引用指向值,你也只能有一个可变的引用如果没有 不可改变的引用的话)。你可以使用可变的引用假设只需要一个不可变引用,但不是反之亦然。把所有的这些概念放在下面这个例子:

fn bar(x: &i32) { ... }
fn bar_mut(x: &mut i32) { ... }  // &mut i32 是一个指向i32的引用,并且可以改变

fn foo() {
    let x = 5;
    //let xr = &mut x;     // 错误 - 无法从一个不可变的变量上创建一个可变的引用

    let xr = &x;           // 可以创建一个不可变的引用

    bar(xr);
    //bar_mut(xr);         // 错误,期望一个可变的引用

    let mut x = 5;
    let xr = &x;           // 可以,创建出一个不可变的引用
    //*xr = 4;             // 错误,试图改变不可变引用
    //let xr = &mut x;     // 错误 - 已经有一个不可变的引用,所以没有办法同时创建一个可变的引用


    let mut x = 5;
    let xr = &mut x;       // 可以,创建一个可变的引用
    *xr = 4;               // Ok
    //let xr = &x;         // 错误 - 已经有一个可变的引用,所以不能同时创建一个不可变的引用
    //let xr = &mut x;     // 错误 - 同时只能有一个可变的引用。
    bar(xr);               // Ok
    bar_mut(xr);           // Ok
}

注意,引用也许可以改变(或不)独立于变量所持有的引用的可变性。这类似于C++的指针可以 是常量(或不)独立的他们所指向的数据。这和所有权指针不同,所有权指针的可变性和数据的可变性是关联在一起的。例如,

fn foo() {
    let mut x = 5;
    let mut y = 6;
    let xr = &mut x;
    //xr = &mut y;        // 错误, xr是不可变的

    let mut x = 5;
    let mut y = 6;
    let mut xr = &mut x;
    xr = &mut y;          // Ok

    let mut x = 5;
    let mut y = 6;
    let mut xr = &x;
    xr = &y;              // 可以 - xr 是可变的,即使所引用的数据不可改变
}

如果一个可变的值是借来的,那么它在借来的期间变成不可变的了。一旦借用的指针超出范围,该值可以被继续改变。这与所有权指针相反,一旦移动就永远不能被再次使用。例如,

fn foo() {
    let mut x = 5;            // type: i32
    {
        let y = &x;           // type: &i32
        //x = 4;              // 错误 - x 已经被借用
        println!("{}", x);    // 可以, x可以读
    }
    x = 4;                    // 可以, y已经不存在了
}

如果我们取一个可变引用的值也会发生同样的事情 - 那个值仍不能修改。Rust一般来说,数据只可以通过 一个变量或指针被修改。此外,当我们有一个可变的引用时,我们不能同时取一个不可改变的引用。这限制了我们如何使用底层的值:

fn foo() {
    let mut x = 5;            // type: i32
    {
        let y = &mut x;       // type: &mut i32
        //x = 4;              // 错误, x已经被借用了
        //println!("{}", x);  // 错误, 需要借用x
    }
    x = 4;                    // 可以,y已经不存在了
}

与C++不同,Rust不会自动为你的对一个值创建引用。因此,如果一个函数通过引用调用一个参数,调用者必须引用实际参数。但是,指针类型将自动转换为引用:

fn foo(x: &i32) { ... }

fn bar(x: i32, y: Box<i32>) {
    foo(&x);
    // foo(x);   // 错误,期望&i32,但找到i32
    foo(y);      // 可以
    foo(&*y);    // 也可以, 并且更显式,但不是一个好的风格
}

mut vs const

在这个阶段,应该值得比较Rust里的mut和C++里的const。 表面上他们是对立的。默认情况下,Rust的值是不可变的,可以通过使用mut来改变值。C++里默认值是可变的,但可以通过使用const来常数化。更微妙、更重要的区别 是C++常量性仅适用于当前使用的值,而Rust的不可变性适用于所有值的使用。所以,在C++里如果我有一个const变量,其他人可以有一个非const引用指向它,他可以改变这个值而不通知我。在Rust里,如果你有一个不可变的变量,Rust会保证这个值不会改变。

正如我们上面提到的,所有可变的变量是独一无二的。所以如果你有一个可变的值,你知道这是不会改变的除非你改变它。此外,你可以自由地改变它,因为你知道,没有任何人正在依靠在它的不变性。

Borrowing and lifetimes 借贷和使用期

Rust的主要安全目标之一是避免悬空指针(一个指针指向一个已释放的内存)。在Rust里,不可能有一个悬空的借贷指针。这是唯一合法的创建借贷指针,只有当所指向内存的存在时间比引用长时(嗯,至少也和引用活的一样长)。换句话说,引用的寿命必须比参考值的寿命要短。

在这篇文章的所有例子中已经展示过。作用域使用{}被导入或者函数绑定在使用期界限-当一个变量超出范围时,它的寿命结束。如果我们试着把一个引用指向一个短寿命的值,例如 在较窄的范围内,编译器会给我们一个错误。例如,

fn foo() {
    let x = 5;
    let mut xr = &x;  // 可以,x和xr都有相同的使用期
    {
        let y = 6;
        //xr = &y     // 错误, xr将比y活的久
    }                 // y在这里被释放
}                     // x和xr在这里被释放

在上面的例子中,x和XR不具有相同的使用期。因为XR开始的比x晚,但是,使用期的终结点更令人感兴趣,因为你在任何情况下都不能引用一个比引用更早死去的变量-Rust强制执行这些东西,这使得它比C++更 安全。

Explicit lifetimes 显式使用期

玩了一段时间借贷指针后,你可能会遇到一个借来的指针有一个明确的使用期。这些语法是&'a T(cf &T)。它们是一个大主题,因为我需要覆盖使用期-多态性在同一时间,所以我将用另外一篇文章来讨论(有几个更不 常见的指针类型需要先提到)。现在,我只想说&T&'a T的速记,当a是当前作用域,这是 声明类型的范围。

results matching ""

    No results matching ""